from random import *

def init(num,length):
    return [[randint(0,1) for i in range(length)] for i in range(num)]

def mutation(x):
    p =  randint(0,len(x))-1
    x[p] = 1- x[p]
    return x

def cross(x,y):
    points = [randint(0,len(x)),randint(0,len(x))]
    a,b = min(points),max(points)
    x[a:b],y[a:b] = y[a:b], x[a:b]
    return (x,y)

def fitness(x):
    x = decoding(x)
    return x**3-60*(x**2)+900*x+100

def decoding(x):
    return int(sum([k*(2**int(i-1)) for i,k in enumerate(x[::-1])]))

def select(pop):
    return choices(pop,weights=list(map(fitness,pop)),k=2)

if __name__ == '__main__':
    pop = init(20,5)
    for i in range(30):
        if i in (0,1,2,28,29):
            table = zip(pop,
                list(map(decoding,pop)),
                list(map(fitness,pop)))
            table = list(table)
            for x,y,z in table:
                print(x,y,z)
            print(max(table,key=lambda x:x[2]))
        new_pop = []
        for i in range(10):
            x_ ,y_ = select(pop)
            [x,y] = cross(x_,y_)
            new_pop.append(x)
            new_pop.append(y)
        for i in new_pop:
            if random() < 0.1:
                i = mutation(i)
        
    table = zip(pop,
        list(map(decoding,pop)),
        list(map(fitness,pop)))
    table = list(table)
    for x,y,z in table:
        print(x,y,z)
    print(max(table,key=lambda x:x[2]))
